<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

class profiler {

	/**
	 * Helper for Kohana::dump(), handles recursion in arrays and objects.
	 *
	 * @param   mixed    variable to dump
	 * @param   integer  maximum length of strings
	 * @param   integer  recursion level (internal)
	 * @return  string
	 */
	protected static function _dump(& $var, $length = 128, $level = 0) {
		if ($var === NULL) {
			return '<small>NULL</small>';
		} elseif (is_bool($var)) {
			return '<small>bool</small> ' . ($var ? 'TRUE' : 'FALSE');
		} elseif (is_float($var)) {
			return '<small>float</small> ' . $var;
		} elseif (is_resource($var)) {
			if (($type = get_resource_type($var)) === 'stream' AND $meta = stream_get_meta_data($var)) {
				$meta = stream_get_meta_data($var);

				if (isset($meta['uri'])) {
					$file = $meta['uri'];

					if (function_exists('stream_is_local')) {
						// Only exists on PHP >= 5.2.4
						if (stream_is_local($file)) {
							$file = Profiler::debug_path($file);
						}
					}

					return '<small>resource</small><span>(' . $type . ')</span> ' . htmlspecialchars($file, ENT_NOQUOTES, 'utf-8');
				}
			} else {
				return '<small>resource</small><span>(' . $type . ')</span>';
			}
		} elseif (is_string($var)) {
			if (strlen($var) > $length) {
				// Encode the truncated string
				$str = htmlspecialchars(substr($var, 0, $length), ENT_NOQUOTES, 'utf-8') . '&nbsp;&hellip;';
			} else {
				// Encode the string
				$str = htmlspecialchars($var, ENT_NOQUOTES, 'utf-8');
			}

			return '<small>string</small><span>(' . strlen($var) . ')</span> "' . $str . '"';
		} elseif (is_array($var)) {
			$output = array();

			// Indentation for this variable
			$space = str_repeat($s = '    ', $level);

			static $marker;

			if ($marker === NULL) {
				// Make a unique marker
				$marker = uniqid("\x00");
			}

			if (empty($var)) {
				// Do nothing
			} elseif (isset($var[$marker])) {
				$output[] = "(\n$space$s*RECURSION*\n$space)";
			} elseif ($level < 5) {
				$output[] = "<span>(";

				$var[$marker] = TRUE;
				foreach ($var as $key => & $val) {
					if ($key === $marker)
						continue;
					if (!is_int($key)) {
						$key = '"' . htmlspecialchars($key, ENT_NOQUOTES, 'utf-8') . '"';
					}

					$output[] = "$space$s$key => " . Profiler::_dump($val, $length, $level + 1);
				}
				unset($var[$marker]);

				$output[] = "$space)</span>";
			} else {
				// Depth too great
				$output[] = "(\n$space$s...\n$space)";
			}

			return '<small>array</small><span>(' . count($var) . ')</span> ' . implode("\n", $output);
		} elseif (is_object($var)) {
			// Copy the object as an array
			$array = (array) $var;

			$output = array();

			// Indentation for this variable
			$space = str_repeat($s = '    ', $level);

			$hash = spl_object_hash($var);

			// Objects that are being dumped
			static $objects = array();

			if (empty($var)) {
				// Do nothing
			} elseif (isset($objects[$hash])) {
				$output[] = "{\n$space$s*RECURSION*\n$space}";
			} elseif ($level < 10) {
				$output[] = "<code>{";

				$objects[$hash] = TRUE;
				foreach ($array as $key => & $val) {
					if ($key[0] === "\x00") {
						// Determine if the access is protected or protected
						$access = '<small>' . ($key[1] === '*' ? 'protected' : 'private') . '</small>';

						// Remove the access level from the variable name
						$key = substr($key, strrpos($key, "\x00") + 1);
					} else {
						$access = '<small>public</small>';
					}

					$output[] = "$space$s$access $key => " . Profiler::_dump($val, $length, $level + 1);
				}
				unset($objects[$hash]);

				$output[] = "$space}</code>";
			} else {
				// Depth too great
				$output[] = "{\n$space$s...\n$space}";
			}

			return '<small>object</small> <span>' . get_class($var) . '(' . count($array) . ')</span> ' . implode("\n", $output);
		} else {
			return '<small>' . gettype($var) . '</small> ' . htmlspecialchars(print_r($var, TRUE), ENT_NOQUOTES, 'utf-8');
		}
	}

	/**
	 * Removes application, system, modpath, or docroot from a filename,
	 * replacing them with the plain text equivalents. Useful for debugging
	 * when you want to display a shorter path.
	 *
	 *     // Displays SYSPATH/classes/kohana.php
	 *     echo Kohana::debug_path(Kohana::find_file('classes', 'kohana'));
	 *
	 * @param   string  path to debug
	 * @return  string
	 */
	public static function debug_path($file)
	{
		if (strpos($file, SYS_PATH) === 0)
		{
			$file = 'SYS_PATH/'.substr($file, strlen(SYS_PATH));
		}

		return $file;
	}

	/**
	 * Returns an HTML string of debugging information about any number of
	 * variables, each wrapped in a "pre" tag:
	 *
	 *     // Displays the type and value of each variable
	 *     echo Kohana::debug($foo, $bar, $baz);
	 *
	 * @param   mixed   variable to debug
	 * @param   ...
	 * @return  string
	 */
	public static function debug() {
		if (func_num_args() === 0)
			return;

		// Get all passed variables
		$variables = func_get_args();

		$output = array();
		foreach ($variables as $var) {
			$output[] = Profiler::_dump($var, 1024);
		}

		return '<pre class="debug">' . implode("\n", $output) . '</pre>';
	}

}

?>
